Skip to content

Fix kanshi compatibility by using output names instead of descriptions#2

Open
zipproth wants to merge 3 commits intopetertheprocess:masterfrom
zipproth:fix-kanshi-output-names
Open

Fix kanshi compatibility by using output names instead of descriptions#2
zipproth wants to merge 3 commits intopetertheprocess:masterfrom
zipproth:fix-kanshi-output-names

Conversation

@zipproth
Copy link
Copy Markdown

Fix kanshi compatibility by using output names instead of descriptions

Problem

The current implementation writes kanshi configurations using output descriptions like:

output "Iiyama North America PL2294H2 1207823601758 (DP-3)" position 840,0 mode 1920x1080@60.0000 scale 1.00 transform 270

This causes kanshi to fail with "no profile matched" because the parentheses in the description make it invalid according to kanshi's parsing rules.

Solution

This PR changes src/store.c to use head->name (e.g. DP-3) instead of head->description, generating valid kanshi configurations like:

output DP-3 position 840,0 mode 1920x1080@60.0000 scale 1.00 transform 270

Technical Details

According to kanshi(5) manual, valid output criteria are:

  • Output names (e.g. "DP-1")
  • Manufacturer/Model/Serial strings (e.g. "Foocorp ASDF 1234")

The current output descriptions containing parentheses like (DP-3) don't match either format.

Testing

Before fix: kanshi shows "no profile matched"
After fix: kanshi shows "applying profile" and works correctly

This resolves the core compatibility issue that prevents the kanshi integration from working.

Kanshi fails to match profiles when output descriptions contain parentheses
like 'Iiyama North America PL2294H2 1207823601758 (DP-3)'. According to
kanshi(5) manual, valid output criteria are:
- Output names (e.g. 'DP-1')
- Manufacturer/Model/Serial without parentheses

This changes store.c to use head->name (e.g. 'DP-3') instead of
head->description, making kanshi profiles work correctly.

Fixes 'no profile matched' error when kanshi tries to apply configurations.
The match() function was still using the old quoted output syntax
("output \"%99[^\"]\"" pattern) which fails to match profiles
written with the new unquoted syntax (output DP-3).

This caused duplicate profiles to be created instead of updating
existing ones. Now supports both quoted (legacy) and unquoted
(current) output formats for backward compatibility.
@petertheprocess
Copy link
Copy Markdown
Owner

which kanshi & sway version are u using?
I have

profile {
    output "Toshiba America Info Systems Inc 43UHD_LCD_TV 0x00000000 (HDMI-A-2)" position 1920,0 mode 4096x2160@30.0000 scale 1.00 transform normal
    output "Chimei Innolux Corporation 0x1245 0x00000000 (eDP-1)" position 2980,2160 mode 1920x1080@60.0010 scale 1.00 transform normal
}
profile {
    output "Unknown HP 727pu CN44211RVS (DP-2)" position 0,0 mode 2560x1440@59.9510 scale 1.00 transform normal
    output "Chimei Innolux Corporation 0x1245 0x00000000 (eDP-1)" position 360,1440 mode 1920x1080@60.0010 scale 1.00 transform normal
}
profile {
    output "Unknown HP 727pu CN44211RVS (DP-1)" position 0,0 mode 2560x1440@59.9510 scale 1.00 transform normal
    output "Chimei Innolux Corporation 0x1245 0x00000000 (eDP-1)" position 640,1440 mode 1920x1080@60.0010 scale 1.00 transform normal
}
profile {
    output "Unknown D221SV-F 16NF006538Q0 (DP-1 via HDMI)" position 1920,0 mode 1920x1080@100.0000 scale 1.00 transform normal
    output "Chimei Innolux Corporation 0x1245 0x00000000 (eDP-1)" position 1920,1080 mode 1920x1080@60.0010 scale 1.00 transform normal
}

Parts of my config are like that, it work and quite stable for me. I have multiple ports and different monitors in office/home/university, you solution tried all different monitors pluged into DP-1 as the same one, which is not a good idea in my opinion.

@zipproth
Copy link
Copy Markdown
Author

zipproth commented Jul 2, 2025

@petertheprocess Thanks for the response! Your configuration works perfectly because you're using the legacy quoted format that the original matching logic supports.

The core issue: There's a mismatch between what wdisplays writes vs what it can read. Currently:

  • wdisplays writes: output DP-3 position ... (unquoted port names)
  • match() function reads: only "Long Description (DP-3)" (quoted descriptions)

This causes the matching to fail, creating duplicate profiles instead of updating existing ones.

What my PR does:

  1. Write format: Changes from head->description to head->name (shorter, cleaner port names)
  2. Read format: Supports both quoted descriptions AND unquoted port names
  3. Backward compatibility: Your existing config will continue to work unchanged

Why this aligns with kanshi spec:
According to kanshi(5), valid output criteria include:

  • Output names: "DP-1"
  • Manufacturer/model/serial: "Company Model Serial"

The current implementation writes invalid hybrid format: "Company Model Serial (DP-3)" which doesn't match either specification.

Your concern about port ambiguity is valid, but using port names (DP-1, DP-3) is actually more stable than descriptions, since the same physical port will always have the same name regardless of which monitor is connected.

The change improves robustness while maintaining full backward compatibility with your existing setup. Would you like me to clarify any part of the implementation?

@petertheprocess
Copy link
Copy Markdown
Owner

petertheprocess commented Jul 8, 2025

@zipproth Thanks for your explaination, now I understand the issue.

I did some search and find it is caused by that:
Starting with Kanshi 1.7, the match_profile_output function was updated to use fnmatch, which changes how output matching works compared to earlier versions (like 1.2.0 I had).

static bool match_profile_output(struct kanshi_profile_output *output,
		struct kanshi_head *head) {
	const char *make = head->make ? head->make : "Unknown";
	const char *model = head->model ? head->model : "Unknown";
	const char *serial_number =
		head->serial_number ? head->serial_number : "Unknown";

	char identifier[1024];
	assert(sizeof(identifier) >= strlen(make) + strlen(model) + strlen(serial_number) + 3);
	snprintf(identifier, sizeof(identifier), "%s %s %s", make, model, serial_number);

	return strcmp(output->name, "*") == 0 ||
		strcmp(output->name, head->name) == 0 ||
		fnmatch(output->name, identifier, 0) == 0;
}

I would like to create a PR for kanshi to make it compatible with legacy quoted format instead of only recording the port name, but you are right your solution is more stable. Anyway, I will look into it more in detail when I have time.

@JasonGantner
Copy link
Copy Markdown

While investigating the consequences of choosing either port name or display serial, I ran into an other related issue:

Modern kanshi syntax can be more complex and the parser from store.c will crash if the syntax is a bit more complex than a list of output profiles, eg:

output "Dell Inc. DELL P2214H 29C295A4527S" mode 1920x1080 alias $dell_left
output "Dell Inc. DELL P2213 Y57VF617BLZS" mode 1680x1050 alias $dell_middle
output eDP-1 mode 1920x1080 alias $internal

profile otg {
    output $internal position 0,0
}

profile desk {
  output $dell_left position 0,0
  output $dell_middle position 1920,30
  output $internal position 1800,1080
}

I believe the long-term solution would be to use the same parser as kanshi : https://codeberg.org/emersion/libscfg

An other (complementary) solution would be to always create our kanshi config file separate from the main config file and use the include directive. This way, complex configurations can be stored in the main config file and simple configurations generated by wdisplays would not interfere.

With the current implementation, we can already have wdisplays write to a separate file just by setting an environment variable.

IMHO it would be better to save profiles from wdisplays using display names rather than output names to allow user-defined generic profiles based on output names without risking erasing them.

@zipproth
Copy link
Copy Markdown
Author

@petertheprocess and @JasonGantner I've updated this PR by merging the latest master into my branch 'fix-kanshi-output-names' to resolve some conflicts and keep it current. Additionally, I created a separate branch 'rebase-for-jason' rebased on @JasonGantner's 'v1.2.0-work' to address his merge request in JasonGantner#4 (see my comment there for details).

Regarding @JasonGantner's suggestions: I agree that using libscfg for a more robust parser would be a great long-term solution to handle complex kanshi syntax (e.g., aliases). Also, saving profiles in a separate file with 'include' is a good idea to avoid interference – the current env variable support already enables this partially. If there's interest, we could discuss integrating that next.

Let me know if this merge looks good or if further adjustments are needed!

Best,
Stefan

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants